/*
 *  $Id: Summary.cs 1063 2010-05-20 17:40:43Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;

namespace Ragima {
    /// <summary>
    /// Суммарный эффект
    /// Содержит итоги действия всех эффектов
    /// </summary>
    public class AffectSummary: ICollector<AbilityCard> {
        private ElementValues _income, _outcome, _incomeLock, _outcomeLock;
        private int _damage;
        private int _minHealth, _maxHealth;
        private int _blocks;
        private int _finishKill;
        private List<AbilityCard> _abilities;
        private List<Affect> _enchantments;
        private List<Affects.LockCards> _lockCard;
        private List<CreaturePredicate> _vanguard;
        private List<Affects.CriticalStrike> _criticals;
        private List<Affects.ModTakenDamage> _takenDamages;
        private List<Affects.ConditionalModDamage> _modDamages;

        public readonly SingleSelectList<Affects.Fast> Fast;
        public readonly SingleSelectList<Affects.Reincarnation> Reincarnation;
        public readonly SingleSelectList<Affects.Fear> Fear;
        public readonly SingleSelectList<Affects.FearAura> FearAura;
        public readonly SingleSelectList<Affects.Undying> Undying;
        public readonly SingleSelectList<Affects.DeathStare> DeathStare;

        public readonly KeyedList<DamageType, Affects.Immune> Immunes;
        public readonly KeyedList<DamageType, Affects.Absorb> DamageAbsorbs;
        public readonly KeyedList<DamageType, Affects.Absorb> HealAbsorbs;

        public readonly PriorityList<IManaCostChanger> ManaChangers;
        public readonly List<Affects.Eating> Eating;
        public readonly List<Affects.StrikeRedirect> StrikeRedirects;

        public readonly FlagDictionary Flags;
        public readonly ActivatableAffectDictionary Activatables;

        public readonly AffectIconDictionary Icons;

        public AffectSummary() {
            _blocks = 1;
            _income = new ElementValues();
            _outcome = new ElementValues();
            _incomeLock = new ElementValues();
            _outcomeLock = new ElementValues();
            _abilities = new List<AbilityCard>();
            _enchantments = new List<Affect>();
            _lockCard = new List<Affects.LockCards>();
            _vanguard = new List<CreaturePredicate>();
            _criticals = new List<Affects.CriticalStrike>();
            _takenDamages = new List<Affects.ModTakenDamage>();
            _modDamages = new List<Affects.ConditionalModDamage>();
            Fast = new SingleSelectList<Affects.Fast>(Affects.Fast.Comparison);
            Reincarnation = new SingleSelectList<Affects.Reincarnation>(Affects.Reincarnation.Comparison);
            Fear = new SingleSelectList<Affects.Fear>(Affects.Fear.Comparison);
            FearAura = new SingleSelectList<Affects.FearAura>(Affects.FearAura.Comparison);
            Undying = new SingleSelectList<Affects.Undying>(Affects.Undying.Comparison);
            DeathStare = new SingleSelectList<Affects.DeathStare>(Affects.DeathStare.Comparison);
            Immunes = new KeyedList<DamageType,Affects.Immune>(Affects.Immune.Getter);
            DamageAbsorbs = new KeyedList<DamageType,Affects.Absorb>(Affects.Absorb.Getter);
            HealAbsorbs = new KeyedList<DamageType,Affects.Absorb>(Affects.Absorb.Getter);
            ManaChangers = new PriorityList<IManaCostChanger>();
            Eating = new List<Affects.Eating>();
            StrikeRedirects = new List<Affects.StrikeRedirect>();
            Flags = new FlagDictionary();
            Activatables = new ActivatableAffectDictionary();
            Icons = new AffectIconDictionary();
        }

        /// <summary>
        /// Установка основного списка способностей
        /// </summary>
        /// <param name="card">Карта, у которой запрашивать способности</param>
        public void SetDefaultAbilities(SummonableCard card) {
            card.CollectAbilities(this);
        }

        void ICollector<AbilityCard>.Add(AbilityCard item) {
            _abilities.Add(item);
        }
        void ICollector<AbilityCard>.Add<D>() {
            _abilities.Add(new D());
        }

#region Методы получения данных
        public int GetManaIncome(ElementType element) {
            return IsManaIncomeLock(element) ? 0 : _income[element];
        }
        public int GetManaOutcome(ElementType element) {
            return IsManaOutcomeLock(element) ? 0 : _outcome[element];
        }
        public int GetManaBalance(ElementType element) {
            return GetManaIncome(element) - GetManaOutcome(element);
        }
        public int GetManaIncomeAll() {
            int sum = 0;
            foreach (ElementType element in ElementType.AllElements)
                sum += GetManaIncome(element);
            return sum;
        }
        public int GetManaOutcomeAll() {
            int sum = 0;
            foreach (ElementType element in ElementType.AllElements)
                sum += GetManaOutcome(element);
            return sum;
        }
        public int GetManaBalanceAll() {
            return GetManaIncomeAll() - GetManaOutcomeAll();
        }
        public bool IsManaIncomeLock(ElementType element) {
            return _incomeLock[element] > 0;
        }
        public bool IsManaOutcomeLock(ElementType element) {
            return _outcomeLock[element] > 0;
        }
        public int GetDamage() {
            return _damage;
        }
        public int GetTakenDamage(DamageType type) {
            int sum = 0;
            foreach (Affects.ModTakenDamage affect in _takenDamages)
                if (affect.DmgType == type || affect.DmgType == null)
                    sum += affect.Value;
            return sum;
        }
        public int GetConditionalDamage(Creature hitter, AbstractObject victim) {
            int sum = 0;
            foreach (Affects.ConditionalModDamage affect in _modDamages)
                if (affect.Accept(hitter, victim))
                    sum += affect.Value;
            return sum;
        }
        public int GetMinHealth() {
            return _minHealth;
        }
        public int GetMaxHealth() {
            return _maxHealth;
        }
        public int GetBlocks() {
            return _blocks;
        }
        public int GetFinishKill() {
            return _finishKill;
        }
        public List<AbilityCard> GetAbilities() {
            return _abilities;
        }
        public List<Affect> GetEnchantments() {
            return _enchantments;
        }
        public bool IsLockCard(Player player, Card card, bool notice) {
            foreach (Affects.LockCards affect in _lockCard)
                if (affect.Condition(player, card)) {
                    if (notice) Game.Current.ErrorNotice(affect.Notice.GetText());
                    return true;
                }
            return false;
        }
        public bool IsVanguard(Creature creature) {
            foreach (CreaturePredicate condition in _vanguard)
                if (condition(creature))
                    return true;
            return false;
        }
        public int GetCriticalBonus() {
            int bonus = 0;
            foreach (Affects.CriticalStrike critical in _criticals)
                bonus += critical.GetCritical();
            return bonus;
        }
#endregion

#region Методы изменения данных
        public void ChangeManaIncome(ElementType element, int value) {
            _income.Increment(element, value);
        }
        public void ChangeManaOutcome(ElementType element, int value) {
            _outcome.Increment(element, value);
        }
        public void SetManaLock(ElementType element, bool income, bool outcome) {
            if (income) _incomeLock.Increment(element);
            if (outcome) _outcomeLock.Increment(element);
        }
        public void ResetManaLock(ElementType element, bool income, bool outcome) {
            if (income) _incomeLock.Decrement(element);
            if (outcome) _outcomeLock.Decrement(element);
        }
        public void ChangeDamage(int value) {
            _damage += value;
        }
        public void AddTakenDamage(Affects.ModTakenDamage affect) {
            _takenDamages.Add(affect);
        }
        public void RemoveTakenDamage(Affects.ModTakenDamage affect) {
            _takenDamages.Remove(affect);
        }
        public void AddConditionalDamage(Affects.ConditionalModDamage affect) {
            _modDamages.Add(affect);
        }
        public void RemoveConditionalDamage(Affects.ConditionalModDamage affect) {
            _modDamages.Remove(affect);
        }
        public void ChangeMinHealth(int value) {
            _minHealth += value;
        }
        public void ChangeMaxHealth(int value) {
            _maxHealth += value;
        }
        public void ChangeBlocks(int value) {
            if (value > 0)
                value--;
            else if (value < 0)
                value++;
            _blocks += value;
        }
        public void ChangeFinishKill(int value) {
            _finishKill += value;
        }
        public void AddAbility(AbilityCard ability) {
            _abilities.Add(ability);
        }
        public void RemoveAbility(AbilityCard ability) {
            _abilities.Remove(ability);
        }
        public void AddEnchantment(Affect affect) {
            _enchantments.Add(affect);
        }
        public void RemoveEnchantment(Affect affect) {
            _enchantments.Remove(affect);
        }
        public void AddLockCard(Affects.LockCards affect) {
            _lockCard.Add(affect);
        }
        public void RemoveLockCard(Affects.LockCards affect) {
            _lockCard.Remove(affect);
        }
        public void AddVanguard(CreaturePredicate condition) {
            _vanguard.Add(condition);
        }
        public void RemoveVanguard(CreaturePredicate condition) {
            _vanguard.Remove(condition);
        }
        public void AddCriticalStrike(Affects.CriticalStrike affect) {
            _criticals.Add(affect);
        }
        public void RemoveCriticalStrike(Affects.CriticalStrike affect) {
            _criticals.Remove(affect);
        }
#endregion

#region События
        public event SimpleEvent OnUpdateAura;
        public event CreatureEvent OnCreatureSummon;
        public event LandEvent OnLandSummon;
        public event AbstractEvent OnDeath;
        public event SimpleEvent OnMeditation;
        public event SimpleEvent OnStartOffencePhase;
        public event SimpleEvent OnEndOffencePhase;
        public event SimpleEvent OnStartDefencePhase;
        public event SimpleEvent OnEndDefencePhase;
        public event CreatureMoveEvent OnMove;
        public event CreatureEvent OnUntap;
        public event CardEvent OnUseCard;
        public event AbilityEvent OnUseAbility;
        public event AttackTryEvent OnAttackTry;
        public event DefenceTryEvent OnDefenceTry;
        public event CreatureEvent OnOffenceReady;
        public event CreatureEvent OnDefenceReady;
        public event ManaChangeEvent OnManaChange;
        public event HitEvent OnHit;
        public event DamageEvent OnBeforeDamage;
        public event DamageEvent OnAfterDamage;
#endregion

#region Методы вызова событий
        public void DoUpdateAura() {
            if (OnUpdateAura != null) OnUpdateAura();
        }
        public void DoCreatureSummon(Creature creature) {
            if (OnCreatureSummon != null) OnCreatureSummon(creature);
        }
        public void DoLandSummon(Land land) {
            if (OnLandSummon != null) OnLandSummon(land);
        }
        public void DoDeath(AbstractObject obj) {
            if (OnDeath != null) OnDeath(obj);
        }
        public void DoMeditation() {
            if (OnMeditation != null) OnMeditation();
        }
        public void DoStartOffencePhase() {
            if (OnStartOffencePhase != null) OnStartOffencePhase();
        }
        public void DoEndOffencePhase() {
            if (OnEndOffencePhase != null) OnEndOffencePhase();
        }
        public void DoStartDefencePhase() {
            if (OnStartDefencePhase != null) OnStartDefencePhase();
        }
        public void DoEndDefencePhase() {
            if (OnEndDefencePhase != null) OnEndDefencePhase();
        }
        public void DoMove(Creature creature, int from, int to) {
            if (OnMove != null) OnMove(creature, from, to);
        }
        public void DoUntap(Creature creature) {
            if (OnUntap != null) OnUntap(creature);
        }
        public void DoUseAbility(AbilityCard ability, TargetArgs args) {
            if (OnUseAbility != null) OnUseAbility(ability, args);
        }
        public void DoUseCard(Card card, TargetArgs args) {
            if (OnUseCard != null) OnUseCard(card, args);
        }
        public void DoAttackTry(Creature attacker, ref bool canAttack) {
            if (OnAttackTry != null) OnAttackTry(attacker, ref canAttack);
        }
        public void DoDefenceTry(Creature defender, Creature attacker, ref bool canDefence) {
            if (OnDefenceTry != null) OnDefenceTry(defender, attacker, ref canDefence);
        }
        public void DoOffenceReady(Creature creature) {
            if (OnOffenceReady != null) OnOffenceReady(creature);
        }
        public void DoDefenceReady(Creature creature) {
            if (OnDefenceReady != null) OnDefenceReady(creature);
        }
        public void DoManaChange(Player player, ElementType element, int value) {
            if (OnManaChange != null) OnManaChange(player, element, value);
        }
        public void DoHit(Creature hitter, AbstractObject target, int damage) {
            if (OnHit != null) OnHit(hitter, target, damage);
        }
        public void DoBeforeDamage(AbstractObject target, DamageInfo damage) {
            if (OnBeforeDamage != null) OnBeforeDamage(target, damage);
        }
        public void DoAfterDamage(AbstractObject target, DamageInfo damage) {
            if (OnAfterDamage != null) OnAfterDamage(target, damage);
        }
#endregion
    }

    [Flags]
    public enum AffectSummaryType {
        Local = 1,
        Global = 2,
        Enemy = 4,
        BothGlobal = Global | Enemy,
    }
}
